/** @file
@brief Declarations related to #PrynGuiControl. */
#ifndef PRYN_GUI_CONTROL_H
#define PRYN_GUI_CONTROL_H

#include <pryn/platform.h>

typedef struct PrynGuiControl PrynGuiControl; ///< A GUI control.
typedef enum PrynGuiCursor PrynGuiCursor;
typedef enum PrynGuiMessageBoxType PrynGuiMessageBoxType;
typedef struct PrynGuiTimer PrynGuiTimer;

/// Double-linked list of PrynGuiTimer.
typedef PrynDList (PrynGuiTimer) PrynGuiTimerList; 

#include <pryn/gui/event.h>
#include <pryn/gui/menu.h>

#include <pryn/result.h>
#include <pryn/state.h>
#include <pryn/utility.h>

/** @defgroup PrynGuiControl PrynGuiControl C API
	@{ */

PrynGuiImport (PrynResult, prynGuiControlCreateWindow, (PrynGuiControl **control, PrynState *state, const PrynString *title, int clientWidth, int clientHeight)) ///< Create a window. This is initially in a hidden state.

PrynGuiImport (PrynResult, prynGuiControlCreateFromHwnd, (PrynGuiControl **control, PrynState *state, void *window)) ///< Create a control from a Microsoft Windows HWND. This returns null if it's not convertable or something else went wrong. The window procedure will be hijacked.

PrynImport (PrynResult, prynGuiControlToObject, (PrynGuiControl *control, PrynObject *object)) ///< Wrap this control in an object.

PrynGuiImport (PrynResult, prynGuiControlRegisterReceiver, (PrynGuiControl *control, PrynGuiEventType type, void *data, PrynGuiReceiverFunction function, PrynGuiReceiverDestroyFunction destroy)) ///< Register an event receiver in the control. All events of the given type will be sent to the function. You can provide a data parameter to pass to the function (it can be zero), and an optional destroy function to free the data (it can also be zero in which case nothing happens when it's deleted).

/** @name Properties
@{ */

	PrynGuiImport (PrynResult, prynGuiControlState, (PrynGuiControl *control, PrynState **output)) ///< State the control exists in.
	PrynGuiImport (PrynResult, prynGuiControlTags, (PrynGuiControl *control, PrynTagList **output)) ///< List of tags associated with the control.
	PrynGuiImport (PrynResult, prynGuiControlHwnd, (PrynGuiControl *control, void **output)) ///< Return the Microsoft Windows HWND handle for the control or return #PrynResultFalse and output 0 if there is none.

/** @} */

PrynGuiImport (PrynResult, prynGuiControlSetCursor, (PrynGuiControl *control, PrynGuiCursor cursor)) ///< Set the cursor to display when the mouse is over the control. This is PrynGuiCursorArrow by default.
PrynGuiImport (PrynResult, prynGuiControlShow, (PrynGuiControl *control)) ///< If the control is hidden, show it.

PrynGuiImport (PrynResult, prynGuiControlGetClientOffsetInScreen, (PrynGuiControl *control, int *x, int *y)) ///< Get the top-left corner of the client area in screen coordinates.
PrynGuiImport (PrynResult, prynGuiControlDirty, (PrynGuiControl *control)) ///< Dirty the entire control so that it needs to be repainted.
PrynGuiImport (PrynResult, prynGuiControlDirtyArea, (PrynGuiControl *control, int left, int top, int right, int bottom)) ///< Dirty a portion of the control so that it will be repainted.
PrynGuiImport (PrynResult, prynGuiControlDirtyRectangle, (PrynGuiControl *control, const PrynRectangle *area)) ///< Dirty a portion of the control so that it will be repainted.

PrynGuiImport (PrynResult, prynGuiReportError, (PrynState *state, PrynResult result)) ///< Report an error code to the user. This generally involves producing a message box.
PrynGuiImport (PrynResult, prynGuiMessageBox, (PrynState *state, const PrynString *title, const PrynString *caption, PrynGuiMessageBoxType type)) ///< Display a message box.

PrynGuiImport (PrynResult, prynGuiControlTimer, (PrynGuiControl *control, int delayMilliseconds, void (*function) (PrynGuiControl *control, void *data), void *data)) ///< Set a timer that will call the function after at least the given number of milliseconds. This is a low-priority message, so it might be sent well after the expected time.

PrynGuiImport (PrynResult, prynGuiControlIsWindow, (PrynGuiControl *control)) ///< Whether the control is a window; #PrynResultTrue if so, or #PrynResultFalse if not.

PrynGuiImport (PrynResult, prynGuiControlSetMenu, (PrynGuiControl *control, PrynGuiMenu *menu)) ///< Assign the menu bar to display at the top of the control. If @e menu is zero, then no menu will be displayed.

PrynGuiImport (PrynResult, prynGuiMessageLoop, (PrynState *state)) ///< Enter a loop receiving and dispatching messages. This is terminated when prynGuiEndMessageLoop is called.
PrynGuiImport (PrynResult, prynGuiQuitMessageLoop, (PrynState *state)) ///< Stop the message loop the next time it's entered. 

/// A timer.
struct PrynGuiTimer
{
	PrynGuiTimer *next; ///< Next timer or 0 if this is the last.
	PrynGuiTimer *previous; ///< Previous timer or 0 if this is the first.
	
	PrynGuiControl *control; ///< Link back to the control.
	int delay; ///< Delay in milliseconds between ticks; 0 if non-repeating.
	void (*function) (PrynGuiControl *control, void *data); ///< Function to call.
	void *data; ///< Data attached to the timer.

#if __cplusplus
#endif /* __cplusplus */
};

/// A standard mouse cursor.
enum PrynGuiCursor
{
	PrynGuiCursorNone = 0, ///< Hide the cursor.
	PrynGuiCursorArrow = 1, ///< Standard arrow cursor. 
	PrynGuiCursorCrosshair = 2, ///< A crosshair used for precise positioning. This can be difficult to impossible to see on a gray background. The fallback is #PrynGuiCursorText.
	PrynGuiCursorDrag = 3, ///< The cursor is being used or can be used to drag around an object. This is usually a hand. The fallback is #PrynGuiCursorArrow.
	PrynGuiCursorHelp = 4, ///< Help is available. This is usually an arrow with a question mark. The fallback is #PrynGuiCursorArrow.
	PrynGuiCursorNo = 5, ///< This action is not allowed. This is usually a circle with a slash through it.
	PrynGuiCursorProcessing = 6, ///< Something is being done, but the program can still be used. For example, this might be a standard arrow with an hourglass. The fallback is #PrynGuiCursorArrow.
	PrynGuiCursorSizeAll = 7, ///< Resize all dimensions at once. This is usually a four-pointed arrow pointing up, down, left, and right.
	PrynGuiCursorSizeLeftUpAndDownRight = 8, ///< Resize diagonally to the up and left and to the down and right. This is usually a two-pointed arrow.
	PrynGuiCursorSizeUpAndDown = 9, ///< Resize to the up and down. This is usually a two-pointed arrow.
	PrynGuiCursorSizeRightUpAndDownLeft = 10, ///< Resize diagonally to the up and right and to the down and left. This is usually a two-pointed arrow.
	PrynGuiCursorSizeLeftAndRight = 11, ///< Resize horizontally left and right. This is usually a two-pointed arrow.
	PrynGuiCursorText = 12, ///< Text selection. This is usually an I-beam, which resembles a capital I. It may be difficult to impossible to see on a gray background. The fallback is #PrynGuiCursorCrosshair.
	PrynGuiCursorWait = 13, ///< An operation is being performed that prevents user input. This may be an hourglass or some more OS-specific image.
	// next: 14
};

#ifdef PrynGuiInternalStructs
/** A GUI control.
*/
struct PrynGuiControl
{
#ifdef PrynGuiInternal
/** @name Internal fields
	These fields are not normally visible to clients, and it is recommended not to use them to maximise binary compatibility. To make them available, define PrynEditorInternal.
	@{ */

	PrynState *pState; ///< State pointer for the control.
	void *pPlatform; ///< Platform-specific information about the control.
	PrynGuiReceiverList pReceivers; ///< Event receivers.
	PrynGuiCursor pCursor; ///< Cursor to use when the mouse is over the control.
	PrynTagList pTags; ///< Tags attached to the control.
	PrynGuiTimerList pTimers; ///< Attached timers.
	PrynGuiMenu *pMenu; ///< Attached menu or 0 for none.

/** @} */
#endif /* PrynGuiInternal */

#ifdef __cplusplus
	inline static PrynGuiControl *(CreateWindow) (PrynState *state, const PrynString &title, int clientWidth, int clientHeight) { PrynGuiControl *result; state->checkError (prynGuiControlCreateWindow (&result, state, &title, clientWidth, clientHeight)); return result; } ///< Create a window. This is initially in a hidden state.

	inline static PrynGuiControl *CreateFromHwnd (PrynState *state, void *window) { PrynGuiControl *result; state->checkError (prynGuiControlCreateFromHwnd (&result, state, window)); return result; }///< Create a control from a Microsoft Windows HWND. The window procedure will be hijacked.

	inline static void ReportError (PrynState *state, PrynResult result) { state->checkError (prynGuiReportError (state, result)); } ///< Report an error code to the user. This generally involves producing a message box. This does so even if the result is #PrynResultSuccess.

	inline static void MessageBox (PrynState *state, const PrynString &title, const PrynString &caption, PrynGuiMessageBoxType type) { state->checkError (prynGuiMessageBox (state, &title, &caption, type)); } ///< Display a message box.

	inline static void MessageLoop (PrynState *state) { state->checkError (prynGuiMessageLoop (state)); } ///< Enter a loop receiving and dispatching messages. This is terminated when #prynGuiQuitMessageLoop/#QuitMessageLoop is called.

	inline static void QuitMessageLoop (PrynState *state) { state->checkError (prynGuiQuitMessageLoop (state)); } ///< Stop the message loop the next time it's entered. 

	inline PrynResult checkError (PrynResult result) { return prynCheckErrorBase (state (), result); } ///< If the result is below zero, throw an error.

	inline PrynObject toObject () { PrynObject result; checkError (prynGuiControlToObject (this, &result)); return result; } ///< Convert into an object wrapper of the control.

	inline void show () { checkError (prynGuiControlShow (this)); } ///< If the control is hidden, show it.
	inline void dirty () { checkError (prynGuiControlDirty (this)); } ///< Dirty the entire control so that it needs to be repainted.
	inline void dirty (int left, int top, int right, int bottom) { checkError (prynGuiControlDirtyArea (this, left, top, right, bottom)); } ///< Dirty a portion of the control so that it will be repainted.
	inline void dirty (const PrynRectangle &area) { checkError (prynGuiControlDirtyRectangle (this, &area)); } ///< Dirty a portion of the control so that it will be repainted.

	inline void clientOffsetInScreen (int *x, int *y) { checkError (prynGuiControlGetClientOffsetInScreen (this, x, y)); } ///< Get the top-left corner of the client area in screen coordinates.

	inline void timer (int delayMilliseconds, void (*function) (PrynGuiControl *control, void *data), void *data) { checkError (prynGuiControlTimer (this, delayMilliseconds, function, data)); } ///< Set a timer that will call the function after at least the given number of milliseconds. This is a low-priority message, so it might be sent well after the expected time.

	inline void registerReceiver (PrynGuiEventType type, void *data, PrynGuiReceiverFunction function, PrynGuiReceiverDestroyFunction destroy = 0) { checkError (prynGuiControlRegisterReceiver (this, type, data, function, destroy)); } ///< Register an event receiver in the control. All events of the given type will be sent to the function. You can provide a data parameter to pass to the function (it can be zero), and an optional destroy function to free the data (it can also be zero in which case nothing happens when it's deleted).

/** @name Properties
@{ */

	inline PrynState *state () { PrynState *result; checkError (prynGuiControlState (this, &result)); return result; } ///< The state this control exists within.
	inline PrynTagList *tags () { PrynTagList *result; checkError (prynGuiControlTags (this, &result)); return result; } ///< Tags associated with the control.
	inline void *hwnd () { void *result; checkError (prynGuiControlHwnd (this, &result)); return result; } ///< The Microsoft Windows HWND this control inhabits or 0 if this is not the same platform.
	inline void cursor (PrynGuiCursor cursor) { checkError (prynGuiControlSetCursor (this, cursor)); } ///< Set the cursor to display when the mouse is over the control. This is #PrynGuiCursorArrow by default.
	inline bool isWindow () { return PrynIsTrue (checkError (prynGuiControlIsWindow (this))); } ///< Whether the control is a window.

/** @} */

#endif /* __cplusplus */
};
#endif /* PrynGuiInternalStructs */

///< Bitmask of types for a message box. There are several distinct parts to this mask - the button and the icon.
enum PrynGuiMessageBoxType
{
	/** @name The Button Field
	@{ */

	PrynGuiMessageBoxTypeButtonShift = 0, ///< Shift to isolate the Button field.
	PrynGuiMessageBoxTypeButtonBits = 4, ///< Number of bits in the Button field.
	PrynGuiMessageBoxTypeButtonMask = ((1 << PrynGuiMessageBoxTypeButtonBits) - 1) << PrynGuiMessageBoxTypeButtonShift, ///< Mask that isolates the Button field.
	PrynGuiMessageBoxTypeButtonStart = 1 << PrynGuiMessageBoxTypeButtonShift, ///< First potential value in the Button field.
	PrynGuiMessageBoxTypeButtonEnd = 1 << (PrynGuiMessageBoxTypeButtonBits + PrynGuiMessageBoxTypeButtonShift), ///< One past the last potential value in the Button field.
	
	PrynGuiMessageBoxTypeOkay = 1 << PrynGuiMessageBoxTypeButtonShift, ///< Show a single OK/Okay button.

	/** @}
	@name The Icon Field
	@{ */
	
	PrynGuiMessageBoxTypeIconShift = PrynGuiMessageBoxTypeButtonShift + PrynGuiMessageBoxTypeButtonBits, ///< Shift to isolate the icon field.
	PrynGuiMessageBoxTypeIconBits = 4, ///< Number of bits in the icon field.
	PrynGuiMessageBoxTypeIconMask = ((1 << PrynGuiMessageBoxTypeIconBits) - 1) << PrynGuiMessageBoxTypeIconShift, ///< Mask that isolates the icon field.
	PrynGuiMessageBoxTypeIconStart = 1 << PrynGuiMessageBoxTypeIconShift, ///< First potential value in the icon field.
	PrynGuiMessageBoxTypeIconEnd = 1 << (PrynGuiMessageBoxTypeIconBits + PrynGuiMessageBoxTypeIconShift), ///< One past the last potential value in the icon field.
	
	PrynGuiMessageBoxTypeIconNone = 0, ///< Display no icon.
	PrynGuiMessageBoxTypeIconError = 1 << PrynGuiMessageBoxTypeIconShift, ///< Display an error icon.

	/** @} */
};

/** @} */

#endif /* PRYN_GUI_CONTROL_H */
